/*
 *  Copyright 2012 Zdeněk Janeček

 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at

 *    http://www.apache.org/licenses/LICENSE-2.0

 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package kiv.pt;

import kiv.pt.ui.IniTrida;

/**
 * Spravuje realitu. Umí nastavit všechny objekty tak, aby odpovídaly realitě.
 * 
 * @author Zdeněk Janeček
 * 
 */
public class Simulator extends Thread {
    // Koeficienty relativního příkonu vůči MAX_PRUM_PRIKON_DOM.
    private static final double[] prikonyDom = { 0.35, 0.3, 0.25, 0.2, 0.15,
            0.3, 0.7, 1.0, 0.9, 0.7, 0.5, 0.5, 0.6, 0.5, 0.5, 0.6, 0.6, 0.7,
            0.9, 1.0, 0.9, 0.7, 0.5, 0.4 };

    // Koeficienty relativního příkonu vůči MAX_PRUM_PRIKON_FIRMA.
    private static final double[] prikonyFirmy = { 0.3, 0.3, 0.3, 0.3, 0.35,
            0.55, 0.7, 0.8, 0.9, 1.0, 1.0, 1.0, 0.85, 1.0, 1.0, 0.9, 0.75, 0.5,
            0.5, 0.4, 0.4, 0.3, 0.3, 0.3 };

    // Jak dlouho čekat na další hodinu.
    private final int DELAY = 3000;

    private boolean running = true;
    private boolean loop = true;
    private int count;
    private int cn;

    /**
     * Příkon v následujícím časovém úseku.
     */
    public double[] futurePrikon;

    // Aktuální čas
    private int time = 0;

    /**
     * Zavolá parametrický konstruktor s parametry count=-1 a time=0.
     */
    public Simulator() {
        this(-1, 0);
    }

    /**
     * Hodiny světa, které mění příkony.
     * 
     * @param count jak dlouho běžet
     * @param time kdy začít
     */
    public Simulator(int count, int time) {
        this.count = count;
        this.time = time - 1;
        if (count == -1) {
            cn = -2;
        }

        futurePrikon = new double[DataGuru.mista.size()];

        for (OdberneMisto m : DataGuru.mista) {
            futurePrikon[m.getId()] = m.isFirma() ? m.getiPrikon()
                    * prikonyFirmy[time] : m.getiPrikon() * prikonyDom[time];
        }
    }

    /**
     * Zastavý běhovou smyčku.
     */
    public synchronized void zastav() {
        loop = false;
    }

    /**
     * Ukáže zda vlákno běží nekonečné smyčce.
     * 
     * @return true v případě že vlákno stále běží
     */
    public boolean isRunning() {
        return loop;
    }

    @Override
    public void run() {
        while (cn < count && loop) {
            try {
                synchronized (this) {
                    if (!running) {
                        wait();
                    }
                }
                Thread.sleep(DELAY);
            } catch (InterruptedException e) {
                loop = false;
                break;
            }
            nextHour();
            String s = "Nyní je " + time + " hodin.";
            IniTrida.setReport(s);

            if (count == -1) {
                continue;
            } else {
                cn++;
            }
        }
        loop = false;
        IniTrida.uklid();
    }

    /**
     * Toggles simulation.
     */
    public synchronized void togglePlay() {
        this.running = !running;

        if (running) {
            notify();
        }
    }

    /**
     * Přesuň se na další hodinu v čase.
     */
    private void nextHour() {
        aktualizujSvet();

        time = (time + 1) % 24;
    }
    
    /**
     * Nastavý pole {@code futurePrikon} na hodnoty v následující hodině.
     */
    public void calculateFuture() {
        for (OdberneMisto m : DataGuru.mista) {
            futurePrikon[m.getId()] = m.isFirma() ? m.getiPrikon()
                    * prikonyFirmy[(time + 1) % 24] : m.getiPrikon() * prikonyDom[(time + 1) % 24];
        }
    }
    
    /**
     * Získej aktuální čas 0-23.
     * 
     * @return čas celočíselná hodnota
     */
    public int getTime() {
        return time;
    }

    /**
     * Posuň se libovolně v čase.
     * 
     * @param time
     *            Čas který potřebujeme.
     */
    public void setTime(int time) {
        if (time >= 0 && time < 24) {
            this.time = time;
            aktualizujSvet();
        }
    }

    /**
     * Nastavý všem odběrným místům příkon relativní k aktualnímu vytížení.
     */
    private void aktualizujSvet() {
        for (OdberneMisto m : DataGuru.mista) {
            m.setPrikon(futurePrikon[m.getId()]);
        }
    }
}
